package com.google.code.contactserv.func;

import com.google.code.contactserv.func.model.ConsoleTest;
import com.google.code.contactserv.func.execution.Executor;

import java.io.*;
import java.util.Iterator;
import java.util.StringTokenizer;
/*
*       Copyright 2007 The semap project development team.
*
*       Licensed under the Apache License, Version 2.0 (the "License");
*       you may not use this file except in compliance with the License.
*       You may obtain a copy of the License at
*
*               http://www.apache.org/licenses/LICENSE-2.0
*
*       Unless required by applicable law or agreed to in writing, software
*       distributed under the License is distributed on an "AS IS" BASIS,
*       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*       See the License for the specific language governing permissions and
*       limitations under the License.
*
*       Created by ${user} at ${date} ${time}.
*
*       ${description}.
*
*       $Id$
*/

public class TestRunner {

    private Executor executor;

    private Iterator<String> inputIterator;
    private Iterator<String> outputIterator;

    private InputStream inputStream;
    private OutputStream outputStream;

    private String errorDescription;
    private PrintWriter out;

    public TestRunner(PrintWriter writer){
        out = writer;
    }
    public void setExecutor(Executor exec) {
        executor = exec;
    }

    synchronized public boolean executeTest(ConsoleTest test) {
        errorDescription = "OK";
        inputIterator = test.getUserInput().iterator();
        outputIterator = test.getExpectedOutput().iterator();

        try {
            executor.execute(test);
            inputStream = executor.getInputStream();
            outputStream = executor.getOutputStream();
            if(!userInterraction()) return false;
            executor.waitFor();
            if (!checkOutput()) return false;
            if (!checkExit(test.getExpectedExitCode(), executor.exitCode())) return false;
        } catch (Exception e) {
            errorDescription = e.toString();
            return false;
        }
        return true;
    }

    public String getExecutionDescription() {
        return errorDescription;
    }

    private boolean checkOutput() throws IOException {
        String actual = readString(inputStream);
        String expected = outputIterator.next();
        return assertEquals(expected, actual);
    }

    private boolean checkExit(int expected, int actual) {
        if (expected != actual) {
            errorDescription = new StringBuilder("Incorrect exit code:")
                    .append("\n").append("Expected: ").append(expected)
                    .append("\n").append("Actual: ").append(actual)
                    .toString();
            return false;
        }
        return true;
    }

    private boolean userInterraction(){
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        boolean flag = true;

        try {
            while(inputIterator.hasNext()){
                StringBuilder sbActual = new StringBuilder();
                String wr = inputIterator.next();
                int i;
                for(i = 0; i < 100; i ++){
                    try{Thread.sleep(100);}catch(InterruptedException e){}
                    sbActual.append(readString(inputStream));
                    if(sbActual.length() == 0 || sbActual.charAt(sbActual.length() - 1) != '>')
                        continue;
                    flag = assertEquals(outputIterator.next(), sbActual.toString());
                    writer.write(wr);
                    writer.flush();
                    out.println(wr);
                    out.flush();
                    break;
                }
                if(i == 100){
                    flag = false;
                    flag = assertEquals(outputIterator.next(), sbActual.toString());
                }
            }
        } catch(IOException e){
            flag = false;
            errorDescription = e.toString();
        }
        return flag;
    }

    private boolean assertEquals(String expected, String actual) {
        StringTokenizer stExpected = new StringTokenizer(expected,"\n");
        StringTokenizer stActual = new StringTokenizer(actual,"\n");
        boolean flag = stExpected.countTokens() == stActual.countTokens();
        while(stExpected.hasMoreTokens()){
            flag = actual.indexOf(stExpected.nextToken()) != -1 && flag;
        }
        if (!flag) {
            errorDescription = new StringBuilder("Incorrect output from tested application:")
                    .append("\n").append("Expected: ").append(expected)
                    .append("\n").append("Actual: ").append(actual)
                    .toString();
            return false;
        }
        return true;
    }

    private String readString(InputStream in) throws IOException{
        StringBuilder sb = new StringBuilder();
        while((in.available() > 0))
          sb.append((char)in.read());
        if(sb.length() != 0){
            out.println(sb.toString());
            out.flush();
        }
        return sb.toString();
    }

}
